`include "fxu_define.vh"

module Mtop(
   o_op_ready, o_result_valid, o_result_bid, o_result_idx, 
   o_result_high, o_result_fexp, o_result_mexp, o_result_exp, 
   o_result_dat, 
   // Inputs
   clk, rst_n, i_cancel, i_op_valid, i_result_ready, i_fpu_cfg_fs, 
   i_fpu_cfg_oe, i_fpu_cfg_ue, i_fpu_cfg_rm_near, i_fpu_cfg_rm_zero, 
   i_fpu_cfg_rm_pinf, i_fpu_cfg_rm_ninf, i_msa_cfg_nx, i_msa_cfg_is, 
   i_msa_cfg_fs, i_msa_cfg_cs, i_msa_cfg_mac2008, i_msa_cfg_nan2008, 
   i_msa_cfg_oe, i_msa_cfg_ue, i_msa_cfg_rm_near, i_msa_cfg_rm_zero, 
   i_msa_cfg_rm_pinf, i_msa_cfg_rm_ninf, i_op_bid, i_op_idx, 
   i_op_high, i_op_fexp, i_op_mexp, i_op_step, i_op_ucode, 
   i_op_dat_a, i_op_dat_b, i_op_dat_c
   );

   
`include "fxu_mul_para.vh"

   input 		 clk;
   input 		 rst_n;

   // control path signals
   input 		 i_cancel;
   input 		 i_op_valid;
   output 		 o_op_ready;
   output 		 o_result_valid;
   input 		 i_result_ready;
   
   // data path signals
   input [UCODE-1:0]     i_op_ucode;
   input [TQ-1:0]        i_op_dat_a;    
   input [TQ-1:0]        i_op_dat_b;
   input [TQ-1:0] 		 i_op_dat_c;    

   output [TQ-1 :0] 	 o_result_dat;


   
   
   /*floating-point config*/
   input                 i_fpu_cfg_fs; //flush to zero subnormal output
   input                 i_fpu_cfg_oe; //overflow enable
   input                 i_fpu_cfg_ue; //underflow enable
   input                 i_fpu_cfg_rm_near; //rounding mode: to nearest
   input                 i_fpu_cfg_rm_zero; //rounding mode: to zero
   input                 i_fpu_cfg_rm_pinf; //rounding mode: to positive infinity
   input                 i_fpu_cfg_rm_ninf; //rounding mode: to negative infinity   
   input                 i_msa_cfg_nx; //non-signaling exception
   input                 i_msa_cfg_is; //flush to zero subnormal input
   input                 i_msa_cfg_fs; //flush to zero subnormal output
   input                 i_msa_cfg_cs; //compare to sNaN signal invaild exception
   input                 i_msa_cfg_mac2008; //IEEE754-2008 Fused multiply-add mode
   input                 i_msa_cfg_nan2008; //IEEE754-2008 sNaN qNaN encoding 
   input                 i_msa_cfg_oe; //overflow enable                    
   input                 i_msa_cfg_ue; //underflow enable                   
   input                 i_msa_cfg_rm_near; //rounding mode: to nearest          
   input                 i_msa_cfg_rm_zero; //rounding mode: to zero             
   input                 i_msa_cfg_rm_pinf; //rounding mode: to positive infinity
   input                 i_msa_cfg_rm_ninf; //rounding mode: to negative infinity

   input [WBID-1:0]      i_op_bid  ;
   input [WIDX-1:0]      i_op_idx  ;
   input                 i_op_high ;
   input                 i_op_fexp ;
   input                 i_op_mexp ;
   input [WSTEP-1:0]     i_op_step ;
   // input [UCODE-1:0]     i_op_ucode;
   
   // input [TQ-1:0]        i_op_dat_a;    
   // input [TQ-1:0]        i_op_dat_b;    

   output  [WBID-1:0]    o_result_bid  ;
   output  [WIDX-1:0]    o_result_idx  ;
   output                o_result_high ;
   output                o_result_fexp ;
   output                o_result_mexp ;
   
   output [EXP-1:0]      o_result_exp;
   // output [TQ-1 :0]      o_result_dat;

/*-------------------------------------------------------------------------------
    fu control signals:
    fu_ctl[4:0] :     [4]                [3]                   [2:1]              [0]
                      sel_fu             sel_fu_1x             sel_dbl/sel_sgl  msa_msub
    
    iu control signals:
    iu_ctl[5:0]       [5]                [4]         
                      sel_iu             sel_iu_1x        
    
                      [3]                [2]                  [1]               [0]
                      sel_iu_mode64      sel_iu_mode32        sel_iu_mode16     sel_iu_mode8
    
    qu control signals:
    qu_ctl[5:0]       [5]                [4]         
                      sel_qu             sel_qu_1x        
    
                      [3]                [2]                  [1]               [0]
                      1'b0               sel_iu_mode32    sel_iu_mode16         1'b0
    
    
    
 ---------------------------------------------------------------------------------*/



   // ==================== control path ====================
   logic [3:0] 			 pipeline_stage_en;
   
   Mfull_pipeline_ctrl #(.STAGE(4)) full_pipeline_ctrl(
													   .o_pipeline_stage_en(pipeline_stage_en), 
													   .o_valid(o_result_valid), 
													   .o_ready(o_op_ready),
													   .i_ready(i_result_ready), 
													   .i_valid(i_op_valid), 
													   .clk(clk),
													   .rst(!rst_n || i_cancel) );
   
   
   // ==================== data path ====================
   // -------------------- the input of pipe stage 0 --------------------
   struct packed {
	  logic [7:0][15:0] op_a, op_b; // for multiply
	  logic 			mul_sign;
	  logic 			mul_mode8;
	  logic 			mul_dotp; // also used in pipe stage 1
	  logic 			mul_qmu; // fixed point mode
	  logic 			mul_round_q;
	  logic 			mul_msub;

	  
	  logic [7:0][15:0] op_c;	// for add/sub (pipe stage 1)
	  logic 			mul_imu;
	  logic				mul_df_b;
	  logic 			mul_pmul;
	  logic 			mul_madd;
	  
   } p0_in, p0_in_r;
   
   assign p0_in.op_a = i_op_dat_a;
   assign p0_in.op_b = i_op_dat_b;
   assign p0_in.mul_sign = i_op_ucode[0] || i_op_ucode[11:8]==`JZ4785_MUL_QMU; // sign or not(when in qmu mode, sign must be 1)
   assign p0_in.mul_mode8 = i_op_ucode[7:4]==`JZ4785_MUL_DF_B || 
							(i_op_ucode[1] && i_op_ucode[11:8]==`JZ4785_MUL_IMU); // byte operation or dotp product
   assign p0_in.mul_dotp = i_op_ucode[1] && i_op_ucode[11:8]==`JZ4785_MUL_IMU; // dot product mode
   assign p0_in.mul_round_q = i_op_ucode[1] && i_op_ucode[11:8]==`JZ4785_MUL_QMU; // round mode for fixedpoint insn
   assign p0_in.mul_qmu = i_op_ucode[11:8] == `JZ4785_MUL_QMU;

   // assign p0_in.op_c = i_op_dat_c;
   assign p0_in.mul_imu = i_op_ucode[11:8] == `JZ4785_MUL_IMU;
   assign p0_in.mul_df_b = i_op_ucode[7:4] == `JZ4785_MUL_DF_B;
   assign p0_in.mul_pmul = i_op_ucode[3:2] == `JZ4785_MUL_PMUL;
   assign p0_in.mul_madd = i_op_ucode[3:2] == `JZ4785_MUL_MADD;
   assign p0_in.mul_msub = i_op_ucode[3:2] == `JZ4785_MUL_MSUB;

   assign p0_in.op_c = p0_in.mul_madd ? i_op_dat_c : p0_in.mul_msub ? ~i_op_dat_c : '0;
   
   
   always_ff@(posedge clk)
   	 begin
   		if(pipeline_stage_en[0])
   		  p0_in_r <= p0_in;
   	 end
   

   
   // -------------------- the input of pipe stage 1 (output of pipe stage 0) --------------------
   struct packed {
   	  logic [7:0][15:0] op_c;	// for add/sub
   	  logic 			mul_imu;
   	  logic 			mul_df_b;
   	  logic 			mul_pmul;
   	  logic 			mul_madd;
   	  logic 			mul_msub;

	  logic 			mul_dotp;
	  
   	  logic [7:0][31:0] mul_final_pp0;
   	  logic [7:0][31:0] mul_final_pp1;
	  logic [7:0][31:0] mul_final_pp2;
	  logic [7:0][31:0] mul_final_pp3;
	  logic [7:0][31:0] mul_final_pp4;
   } p0_out, p1_in_r;
   

   genvar 				i;
   generate
   	  for(i=0;i<8;i++)
   		begin : muls
   		   Mfxu_mul_mix fxu_mul_mix(			// visit by [top].muls[i].multiply.xxx
   												.o_final_pp0(p0_out.mul_final_pp0[i]),
   												.o_final_pp1(p0_out.mul_final_pp1[i]),
												.o_final_pp2(p0_out.mul_final_pp2[i]),
												.o_final_pp3(p0_out.mul_final_pp3[i]),
												.o_final_pp4(p0_out.mul_final_pp4[i]),
   												.i_a(p0_in_r.op_a[i]),
   												.i_b(p0_in_r.op_b[i]),
												.i_c(p0_in_r.op_c[i]),
   												.i_sign(p0_in_r.mul_sign),
   												.i_mode8(p0_in_r.mul_mode8),
   												.i_dotp(p0_in_r.mul_dotp),
												.i_madd(p0_in_r.mul_madd),
												.i_msub(p0_in_r.mul_msub),
												.i_mode_q(p0_in_r.mul_qmu),
   												.i_round_q(p0_in_r.mul_round_q)
   												);

   		end
   endgenerate
   
   // -------------------- the input of pipe stage 1 --------------------      
   assign p0_out.op_c = p0_in_r.op_c;
   assign p0_out.mul_imu = p0_in_r.mul_imu;
   assign p0_out.mul_df_b = p0_in_r.mul_df_b;
   assign p0_out.mul_pmul = p0_in_r.mul_pmul;
   assign p0_out.mul_madd = p0_in_r.mul_madd;
   assign p0_out.mul_msub = p0_in_r.mul_msub;
   assign p0_out.mul_dotp = p0_in_r.mul_dotp;
   
   always_ff@(posedge clk)
   	 begin
   		if(pipeline_stage_en[1])
   		  p1_in_r <= p0_out;
   	 end

   // -------------------- the input of pipe stage 2(output of pipe stage 1) --------------------      
   struct packed {
	  logic [7:0][31:0] result;

	  logic 			mul_imu;
	  logic 			mul_msub;
   	  logic 			mul_df_b;
   } p1_out, p2_in_r;

   assign p1_out.mul_imu = p1_in_r.mul_imu;
   assign p1_out.mul_msub = p1_in_r.mul_msub;
   assign p1_out.mul_df_b = p1_in_r.mul_df_b;
   
   generate
   	  for(i=0;i<8;i++)
   		begin : addsubs
		   
		   logic [31:0] final_pp0, final_pp1;
		   
		   DW02_tree #( .num_inputs(5),
   		   				.input_width(32) 
   		   				) U_wallace (
   		   							 .INPUT( {p1_in_r.mul_final_pp0[i],
		   									  p1_in_r.mul_final_pp1[i],
		   									  p1_in_r.mul_final_pp2[i],
		   									  p1_in_r.mul_final_pp3[i],
		   									  p1_in_r.mul_final_pp4[i]} ),
   		   							 .OUT0(final_pp0),
   		   							 .OUT1(final_pp1) );
		   
		   assign p1_out.result[i] = final_pp0 + final_pp1;

		   // assign p1_out.result[i] = p1_in_r.mul_final_pp0[i] + 
		   // 							 p1_in_r.mul_final_pp1[i] + 
		   // 							 p1_in_r.mul_final_pp2[i] + 
		   // 							 p1_in_r.mul_final_pp3[i] + 
		   // 							 p1_in_r.mul_final_pp4[i];
   		end
   endgenerate
   
   
   
   always_ff@(posedge clk)
   	 begin
   		if(pipeline_stage_en[2])
   		  p2_in_r <= p1_out;
   	 end
   
   // -------------------- the output of pipe stage 2 --------------------
   logic [TQ-1:0] p2_out, p2_out_r;
   generate
   	  for(i=0;i<8;i++)
   		begin
		   logic [31:0] result, result_imu, result_qmu;

		   assign result = p2_in_r.mul_msub ? ~p2_in_r.result[i] : p2_in_r.result[i];
		   assign result_imu = p2_in_r.mul_df_b ? {result[23:16],result[7:0]} : result[15:0];
		   // saturate for fixed point insn
		   assign result_qmu = (result[31:30]==2'b01) ? {1'b0, 15'h7fff} :
		   					   (result[31:30]==2'b10) ? {1'b1, 15'h0} :
		   					   result[30:15];
		   assign p2_out[i*16+:16] = p2_in_r.mul_imu ? result_imu : result_qmu;
		   
   		end
   endgenerate
   

   // -------------------- output --------------------
   always_ff@(posedge clk)
	 begin
		if(pipeline_stage_en[3])
		  p2_out_r <= p2_out;
	 end
   
   assign o_result_dat = p2_out_r;
   

endmodule // Mfxu_mul_top

